<!DOCTYPE html>

<html lang="en">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width">
	<title>Common Modules Source: pointer/TouchSource.js</title>

	<!--[if lt IE 9]>
	<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
	<![endif]-->
	<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">

	<link type="text/css" rel="stylesheet" href="styles/site.simplex.css">

</head>

<body>

<div class="navbar navbar-default navbar-fixed-top navbar-inverse">
<div class="container">
	<div class="navbar-header">
		<a class="navbar-brand" href="index.html">Common Modules</a>
		<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#topNavigation">
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
        </button>
	</div>
	<div class="navbar-collapse collapse" id="topNavigation">
		<ul class="nav navbar-nav">
			
			<li class="dropdown">
				<a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b class="caret"></b></a>
				<ul class="dropdown-menu ">
					<li><a href="Base.html">Base</a></li><li><a href="Event.html">Event</a></li><li><a href="Html.html">Html</a></li><li><a href="Internal.html">Internal</a></li><li><a href="ol.Overlay.Popup.html">ol.Overlay.Popup</a></li>
				</ul>
			</li>
			
			<li class="dropdown">
				<a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b class="caret"></b></a>
				<ul class="dropdown-menu ">
					<li><a href="global.html#addClass">addClass</a></li><li><a href="global.html#addListener">addListener</a></li><li><a href="global.html#arrayRemove">arrayRemove</a></li><li><a href="global.html#clear">clear</a></li><li><a href="global.html#create">create</a></li><li><a href="global.html#createHidden">createHidden</a></li><li><a href="global.html#empty">empty</a></li><li><a href="global.html#EventType">EventType</a></li><li><a href="global.html#findListener">findListener</a></li><li><a href="global.html#firstUpperToCase">firstUpperToCase</a></li><li><a href="global.html#get">get</a></li><li><a href="global.html#getChildByTagName">getChildByTagName</a></li><li><a href="global.html#getClass">getClass</a></li><li><a href="global.html#getDomEventKey">getDomEventKey</a></li><li><a href="global.html#getElementsByClassName">getElementsByClassName</a></li><li><a href="global.html#getListeners">getListeners</a></li><li><a href="global.html#getrandom">getrandom</a></li><li><a href="global.html#getStyle">getStyle</a></li><li><a href="global.html#getuuid">getuuid</a></li><li><a href="global.html#hasClass">hasClass</a></li><li><a href="global.html#isObject">isObject</a></li><li><a href="global.html#isString">isString</a></li><li><a href="global.html#layerConfigDefautls">layerConfigDefautls</a></li><li><a href="global.html#listen">listen</a></li><li><a href="global.html#listenOnce">listenOnce</a></li><li><a href="global.html#logo">logo</a></li><li><a href="global.html#OL_Style_Type">OL_Style_Type</a></li><li><a href="global.html#remove">remove</a></li><li><a href="global.html#removeClass">removeClass</a></li><li><a href="global.html#removeListener">removeListener</a></li><li><a href="global.html#removeListeners">removeListeners</a></li><li><a href="global.html#setClass">setClass</a></li><li><a href="global.html#splitWords">splitWords</a></li><li><a href="global.html#stamp">stamp</a></li><li><a href="global.html#toBack">toBack</a></li><li><a href="global.html#toFront">toFront</a></li><li><a href="global.html#trim">trim</a></li><li><a href="global.html#unListen">unListen</a></li><li><a href="global.html#unlistenAll">unlistenAll</a></li><li><a href="global.html#unListenByKey">unListenByKey</a></li><li><a href="global.html#upperFirstChart">upperFirstChart</a></li>
				</ul>
			</li>
			
		</ul>
        
            <div class="col-sm-3 col-md-3">
                <form class="navbar-form" role="search">
                    <div class="input-group">
                        <input type="text" class="form-control" placeholder="Search" name="q" id="search-input">
                        <div class="input-group-btn">
                            <button class="btn btn-default" id="search-submit"><i class="glyphicon glyphicon-search"></i></button>
                        </div>
                    </div>
                </form>
            </div>
        
	</div>

</div>
</div>


<div class="container" id="toc-content">
<div class="row">

	
	<div class="col-md-12">
	
		<div id="main">
			

		<h1 class="page-title">Source: pointer/TouchSource.js</h1>
    
<section>
    <article>
        <pre
            class="sunlight-highlight-javascript linenums">// Based on https://github.com/Polymer/PointerEvents

import EventSource from './EventSource'
import MouseSource from './MouseSource'
import {arrayRemove} from '../utils/utils'
class TouchSource extends EventSource {
  constructor (dispatcher, mouseSource) {
    super()
    let mapping = {
      'touchstart': this.touchstart,
      'touchmove': this.touchmove,
      'touchend': this.touchend,
      'touchcancel': this.touchcancel
    }

    /**
     * call
     */
    EventSource.call(this, dispatcher, mapping)

    /**
     * @const
     * @type {!Object.&lt;string, Event|Object>}
     */
    this.pointerMap = dispatcher.pointerMap

    /**
     * @const
     * @type {MouseSource}
     */
    this.mouseSource = mouseSource

    /**
     * @private
     * @type {number|undefined}
     */
    this.firstTouchId_ = undefined

    /**
     * @private
     * @type {number}
     */
    this.clickCount_ = 0

    /**
     * @private
     * @type {number|undefined}
     */
    this.resetId_ = undefined
  }

  /**
   * Mouse event timeout: This should be long enough to
   * ignore compat mouse events made by touch.
   * @const
   * @type {number}
   */
  static DEDUP_TIMEOUT = 2500

  /**
   * @const
   * @type {number}
   */
  static CLICK_COUNT_TIMEOUT = 200

  /**
   * @const
   * @type {string}
   */
  static POINTER_TYPE = 'touch'

  /**
   * @private
   * @param {Touch} inTouch The in touch.
   * @return {boolean} True, if this is the primary touch.
   */
  isPrimaryTouch_ (inTouch) {
    return this.firstTouchId_ === inTouch.identifier
  }

  /**
   * Set primary touch if there are no pointers, or the only pointer is the mouse.
   * @param {Touch} inTouch The in touch.
   * @private
   */
  setPrimaryTouch_ (inTouch) {
    let count = Object.keys(this.pointerMap).length
    if (count === 0 || (count === 1 &amp;&amp;
      MouseSource.POINTER_ID.toString() in this.pointerMap)) {
      this.firstTouchId_ = inTouch.identifier
      this.cancelResetClickCount_()
    }
  }

  /**
   * @private
   * @param {Object} inPointer The in pointer object.
   */
  removePrimaryPointer_ (inPointer) {
    if (inPointer.isPrimary) {
      this.firstTouchId_ = undefined
      this.resetClickCount_()
    }
  }

  /**
   * @private
   */
  resetClickCount_ () {
    this.resetId_ = setTimeout(
      this.resetClickCountHandler_.bind(this),
      TouchSource.CLICK_COUNT_TIMEOUT)
  }

  /**
   * @private
   */
  resetClickCountHandler_ () {
    this.clickCount_ = 0
    this.resetId_ = undefined
  }

  /**
   * @private
   */
  cancelResetClickCount_ () {
    if (this.resetId_ !== undefined) {
      clearTimeout(this.resetId_)
    }
  }

  /**
   * @private
   * @param {Event} browserEvent Browser event
   * @param {Touch} inTouch Touch event
   * @return {Object} A pointer object.
   */
  touchToPointer_ (browserEvent, inTouch) {
    let e = this.dispatcher.cloneEvent(browserEvent, inTouch)
    e.pointerId = inTouch.identifier + 2
    // TODO: check if this is necessary?
    e.bubbles = true
    e.cancelable = true
    e.detail = this.clickCount_
    e.button = 0
    e.buttons = 1
    e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0
    e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0
    e.pressure = inTouch.webkitForce || inTouch.force || 0.5
    e.isPrimary = this.isPrimaryTouch_(inTouch)
    e.pointerType = TouchSource.POINTER_TYPE
    // make sure that the properties that are different for
    // each `Touch` object are not copied from the BrowserEvent object
    e.clientX = inTouch.clientX
    e.clientY = inTouch.clientY
    e.screenX = inTouch.screenX
    e.screenY = inTouch.screenY
    return e
  }

  /**
   * @private
   * @param {Event} inEvent Touch event
   * @param {function(Event, Object)} inFunction In function.
   */
  processTouches_ (inEvent, inFunction) {
    let touches = Array.prototype.slice.call(inEvent.changedTouches)
    let count = touches.length
    function preventDefault () {
      inEvent.preventDefault()
    }
    let pointer
    for (let i = 0; i &lt; count; ++i) {
      pointer = this.touchToPointer_(inEvent, touches[i])
      // forward touch preventDefaults
      pointer.preventDefault = preventDefault
      inFunction.call(this, inEvent, pointer)
    }
  }

  /**
   * @private
   * @param {TouchList} touchList The touch list.
   * @param {number} searchId Search identifier.
   * @return {boolean} True, if the `Touch` with the given id is in the list.
   */
  findTouch_ (touchList, searchId) {
    let [l, touch] = [touchList.length]
    for (let i = 0; i &lt; l; i++) {
      touch = touchList[i]
      if (touch.identifier === searchId) {
        return true
      }
    }
    return false
  }

  /**
   * In some instances, a touchstart can happen without a touchend. This
   * leaves the pointermap in a broken state.
   * Therefore, on every touchstart, we remove the touches that did not fire a
   * touchend event.
   * To keep state globally consistent, we fire a pointercancel for
   * this "abandoned" touch
   * @param inEvent
   * @private
   */
  vacuumTouches_ (inEvent) {
    let touchList = inEvent.touches
    let keys = Object.keys(this.pointerMap)
    let count = keys.length
    if (count >= touchList.length) {
      var d = []
      var i, key, value
      for (i = 0; i &lt; count; ++i) {
        key = keys[i]
        value = this.pointerMap[key]
        if (key !== MouseSource.POINTER_ID &amp;&amp;
          !this.findTouch_(touchList, key - 2)) {
          d.push(value.out)
        }
      }
      for (i = 0; i &lt; d.length; ++i) {
        this.cancelOut_(inEvent, d[i])
      }
    }
  }

  /**
   * Handler for `touchstart`, triggers `pointerover`,
   * `pointerenter` and `pointerdown` events.
   * @param {Event} inEvent The in event.
   */
  touchstart (inEvent) {
    this.vacuumTouches_(inEvent)
    this.setPrimaryTouch_(inEvent.changedTouches[0])
    this.dedupSynthMouse_(inEvent)
    this.clickCount_++
    this.processTouches_(inEvent, this.overDown_)
  }

  /**
   * @private
   * @param {Event} browserEvent The event.
   * @param {Object} inPointer The in pointer object.
   */
  overDown_ (browserEvent, inPointer) {
    this.pointerMap[inPointer.pointerId] = {
      target: inPointer.target,
      out: inPointer,
      outTarget: inPointer.target
    }
    this.dispatcher.over(inPointer, browserEvent)
    this.dispatcher.enter(inPointer, browserEvent)
    this.dispatcher.down(inPointer, browserEvent)
  }

  /**
   * Handler for `touchmove`.
   * @param {Event} inEvent The in event.
   */
  touchmove (inEvent) {
    inEvent.preventDefault()
    this.processTouches_(inEvent, this.moveOverOut_)
  }

  /**
   * @private
   * @param {Event} browserEvent The event.
   * @param {Object} inPointer The in pointer.
   */
  moveOverOut_ (browserEvent, inPointer) {
    let event = inPointer
    let pointer = this.pointerMap[event.pointerId]
    // a finger drifted off the screen, ignore it
    if (!pointer) {
      return
    }
    let outEvent = pointer.out
    let outTarget = pointer.outTarget
    this.dispatcher.move(event, browserEvent)
    if (outEvent &amp;&amp; outTarget !== event.target) {
      outEvent.relatedTarget = event.target
      event.relatedTarget = outTarget
      // recover from retargeting by shadow
      outEvent.target = outTarget
      if (event.target) {
        this.dispatcher.leaveOut(outEvent, browserEvent)
        this.dispatcher.enterOver(event, browserEvent)
      } else {
        // clean up case when finger leaves the screen
        event.target = outTarget
        event.relatedTarget = null
        this.cancelOut_(browserEvent, event)
      }
    }
    pointer.out = event
    pointer.outTarget = event.target
  }

  /**
   * Handler for `touchend`, triggers `pointerup`,
   * `pointerout` and `pointerleave` events.
   * @param {Event} inEvent The event.
   */
  touchend (inEvent) {
    this.dedupSynthMouse_(inEvent)
    this.processTouches_(inEvent, this.upOut_)
  }

  /**
   * @private
   * @param {Event} browserEvent An event.
   * @param {Object} inPointer The inPointer object.
   */
  upOut_ (browserEvent, inPointer) {
    this.dispatcher.up(inPointer, browserEvent)
    this.dispatcher.out(inPointer, browserEvent)
    this.dispatcher.leave(inPointer, browserEvent)
    this.cleanUpPointer_(inPointer)
  }

  /**
   * Handler for `touchcancel`, triggers `pointercancel`,
   * `pointerout` and `pointerleave` events.
   * @param {Event} inEvent The in event.
   */
  touchcancel (inEvent) {
    this.processTouches_(inEvent, this.cancelOut_)
  }

  /**
   * @private
   * @param {Event} browserEvent The event.
   * @param {Object} inPointer The in pointer.
   */
  cancelOut_ (browserEvent, inPointer) {
    this.dispatcher.cancel(inPointer, browserEvent)
    this.dispatcher.out(inPointer, browserEvent)
    this.dispatcher.leave(inPointer, browserEvent)
    this.cleanUpPointer_(inPointer)
  }

  /**
   * @private
   * @param {Object} inPointer The inPointer object.
   */
  cleanUpPointer_ (inPointer) {
    delete this.pointerMap[inPointer.pointerId]
    this.removePrimaryPointer_(inPointer)
  }

  /**
   * Prevent synth mouse events from creating pointer events.
   *
   * @private
   * @param {Event} inEvent The in event.
   */
  dedupSynthMouse_ (inEvent) {
    let lts = this.mouseSource.lastTouches
    let t = inEvent.changedTouches[0]
    if (this.isPrimaryTouch_(t)) {
      let lt = [t.clientX, t.clientY]
      lts.push(lt)
      setTimeout(function () {
        // remove touch after timeout
        arrayRemove(lts, lt)
      }, TouchSource.DEDUP_TIMEOUT)
    }
  }
}

export default TouchSource
</pre>
    </article>
</section>





		</div>
	</div>

	<div class="clearfix"></div>

	

</div>
</div>


    <div class="modal fade" id="searchResults">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title">Search results</h4>
          </div>
          <div class="modal-body"></div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div>


<footer>


	<span class="copyright">
	https://lzw.me.
	</span>

<span class="jsdoc-message">
	Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
	
		on Sun Aug 12th 2018
	
	using the <a href="https://github.com/docstrap/docstrap">DocStrap template</a>.
</span>
</footer>

<script src="scripts/docstrap.lib.js"></script>
<script src="scripts/toc.js"></script>

    <script type="text/javascript" src="scripts/fulltext-search-ui.js"></script>


<script>
$( function () {
	$( "[id*='$']" ).each( function () {
		var $this = $( this );

		$this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
	} );

	$( ".tutorial-section pre, .readme-section pre, pre.prettyprint.source" ).each( function () {
		var $this = $( this );

		var example = $this.find( "code" );
		exampleText = example.html();
		var lang = /{@lang (.*?)}/.exec( exampleText );
		if ( lang && lang[1] ) {
			exampleText = exampleText.replace( lang[0], "" );
			example.html( exampleText );
			lang = lang[1];
		} else {
			var langClassMatch = example.parent()[0].className.match(/lang\-(\S+)/);
			lang = langClassMatch ? langClassMatch[1] : "javascript";
		}

		if ( lang ) {

			$this
			.addClass( "sunlight-highlight-" + lang )
			.addClass( "linenums" )
			.html( example.html() );

		}
	} );

	Sunlight.highlightAll( {
		lineNumbers : true,
		showMenu : true,
		enableDoclinks : true
	} );

	$.catchAnchorLinks( {
        navbarOffset: 10
	} );
	$( "#toc" ).toc( {
		anchorName  : function ( i, heading, prefix ) {
			return $( heading ).attr( "id" ) || ( prefix + i );
		},
		selectors   : "#toc-content h1,#toc-content h2,#toc-content h3,#toc-content h4",
		showAndHide : false,
		smoothScrolling: true
	} );

	$( "#main span[id^='toc']" ).addClass( "toc-shim" );
	$( '.dropdown-toggle' ).dropdown();

    $( "table" ).each( function () {
      var $this = $( this );
      $this.addClass('table');
    } );

} );
</script>



<!--Navigation and Symbol Display-->


<!--Google Analytics-->



    <script type="text/javascript">
        $(document).ready(function() {
            SearcherDisplay.init();
        });
    </script>


</body>
</html>
